package com.smedi.ismedi.file.core.port.adapter.service.Impl;

import cn.hutool.core.lang.UUID;
import com.smedi.ismedi.file.core.domain.aggregatemodel.SignatureCommand;
import com.smedi.ismedi.file.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.file.core.port.adapter.service.CallOtherApiService;
import com.smedi.ismedi.file.core.port.adapter.service.response.Base64Info;
import com.smedi.ismedi.file.core.port.adapter.service.response.FileInfoBase64;
import com.smedi.ismedi.file.core.port.adapter.service.response.Response;
import com.smedi.ismedi.web.starter.exception.LogicException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.nio.file.Files;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class CallOtherApiServiceImpl implements CallOtherApiService {
    @Value("${token.url}")
    private String tokenUrl;

    @Value("${token.username}")
    private String username;

    @Value("${token.password}")
    private String password;

    @Value("${signature.url}")
    private String signatureUrl;

    @Override
    public String getToken() {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(30);
        RestTemplate restTemplate = new RestTemplate(simpleClientHttpRequestFactory);
        // 创建头参数
        HttpHeaders headers = new HttpHeaders();
        MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        // 接口参数
        map.add("userName", username);
        map.add("password", password);
        // 使用Form Data传参
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 构造实体对象
        HttpEntity<MultiValueMap<String, Object>> param = new HttpEntity<>(map, headers);
        // 发起请求,服务地址，请求参数，返回消息体的数据类型
        ResponseEntity<Object> response = restTemplate.exchange(tokenUrl, HttpMethod.POST, param, Object.class);
        // 界面会返回Restful格式数据
        boolean success = (Boolean) ((LinkedHashMap) response.getBody()).get("success");
        String errorMsg = (String) ((LinkedHashMap) response.getBody()).get("errorMsg");
        if (success) {
            // 获取数据
            Map<String, Object> data = (Map<String, Object>) ((LinkedHashMap) response.getBody()).get("data");
            return String.valueOf(data.get("token"));
        } else {
            // 显示报错
            log.error("token获取失败{}", errorMsg);
            throw LogicException.raise(CustomError.TOKEN_GET_FAIL);
        }
    }

    @Override
    public File getSignature(String token, File inputFile, List<SignatureCommand> signatureCommands) {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(30);
        RestTemplate restTemplate = new RestTemplate(simpleClientHttpRequestFactory);

        String base64 = fileToBase64(inputFile);
        Map<String, Object> dataMap = new HashMap<>();

        // 构成请求参数
        MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
        params.add("strict", true);  // 是否强校验
        params.add("base64", base64); // pdf解析成base64

        // 创建签章数据
        for (SignatureCommand signatureCommand : signatureCommands) {
            Map<String, Object> sign = new HashMap<>();
            String temp = "PER" + signatureCommand.getWorkerId().toString();
            sign.put("keysn", temp);
            sign.put("signName", temp);
            sign.put("pagen", 0); // 签章页 全部签章
            dataMap.put(signatureCommand.getSignLocation(), sign);
            params.add("signatureMaps", dataMap);
        }
        // 坐标定位关键字进行敲章
        // 若有其它签章信息，以此类推叠加
        String batchInfo = UUID.fastUUID().toString();
        params.add("batchInfo", batchInfo);
        params.add("blend", "BM_NORMAL");
        // 发送请求
        HttpHeaders headers = new HttpHeaders();
        HttpMethod method = HttpMethod.POST;
        // 以表单的方式提交
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        // 添加token
        headers.add("access-token", token);
        // 将请求头部和参数合成一个请求
        HttpEntity<Object> requestEntity = new HttpEntity<>(params, headers);
        // 执行HTTP请求，将返回的结构使用ResultVO类格式化
        Optional<ResponseEntity<Response>> exchange = Optional.of(restTemplate.exchange(signatureUrl, method, requestEntity, Response.class));
        if (!exchange.map(HttpEntity::getBody).map(Response::isSuccess).orElse(false)) {
            log.info("调用签章失败，{}", exchange.map(HttpEntity::getBody).map(Response::getErrorMsg));
        }
        String base64Return = exchange.map(HttpEntity::getBody).map(Response::getData).map(FileInfoBase64::getBase64Info).map(Base64Info::getBase64Buffer).orElseThrow(() -> LogicException.raise(CustomError.REMOTE_SIGNATURE_FAIL));

        return base64ToFile(base64Return);
    }

    private File base64ToFile(String data64) {
        String data = data64.replace("\r\n", "");
        File pdf = null;
        String tempFileName = java.util.UUID.randomUUID().toString();

        try {
            // 临时文件
            pdf = File.createTempFile(tempFileName, ".pdf");
            try (java.io.FileOutputStream fos = new FileOutputStream(pdf);
                 BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                BASE64Decoder decoder = new BASE64Decoder();
                byte[] bytes = decoder.decodeBuffer(data);
                bos.write(bytes);
            }
        } catch (Exception e) {
            try {
                assert pdf != null;
                Files.deleteIfExists(pdf.toPath());
            } catch (Exception exception) {
                log.info("临时文件删除失败");
            }
            e.printStackTrace();
        }

        return pdf;
    }

    private static String fileToBase64(File file) {
        BASE64Encoder encoder = new BASE64Encoder();
        if (file == null) {
            return null;
        }
        String base64 = null;
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(file);
            byte[] buff = new byte[fin.available()];
            fin.read(buff);
            base64 = encoder.encode(buff);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fin != null) {
                try {
                    fin.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return base64;
    }
}
